package com.juc.lock;

import java.util.concurrent.TimeUnit;

/**
 * @Author Zan
 * @Create 2024/5/7 14:53
 * @ClassName : Lock8Demo
 * @Description :
 *      题目：谈谈你对多线程锁的理解，8锁案例说明
 *      口诀：线程  操作  资源类
 *      8锁案例说明：
 *          1.标准访问有ab两个线程(a先启动，邮件)，请问先打印邮件还是短信 - 先打印 邮件
 *          2.sendEmail方法中加入暂停3s - 先打印 邮件
 *          3.添加一个普通的hello方法，先打印邮件还是hello - 先打印 hello
 *          4.有两部手机，请问先打印邮件还是短信 - 先打印 短信
 *          5.有两个静态同步方法，有1部手机，请问先打印邮件还是短信 - 先打印 邮件
 *          6.有两个静态同步方法，有2部手机，请问先打印邮件还是短信 - 先打印 邮件
 *          7.有一个静态同步方法，一个普通同步方法，1部手机，请问先打印邮件还是短信 - 先打印 短信
 *          8.有一个静态同步方法，一个普通同步方法，2部手机，请问先打印邮件还是短信 - 先打印 短信
 *
 *      笔记总结：
 *          1-2
 *          一个对象里面如果有多个synchronized方法，某一个时刻内，只要一个线程去调用其中的一个synchronized方法了，其他的线程都只能等待
 *          换句话说，某一个时刻内，只能有唯一的一个线程去访问这些synchronized方法
 *          锁的是当前对象this，被锁定后，其他的线程都不能进入到当前对象的其他的synchronized方法
 *
 *          3-4
 *          加个普通方法后发现与同步锁无关
 *          换成两个对象后，不是同一把锁了，情况立刻发生变化
 *
 *          5-6 都换成静态同步方法后，情况有变化
 *          三种 synchronized 锁的内容有一些差别:
 *          对于普通同步方法，锁的是当前实例对象，通常指this，具体的一部部手机，所有的普通同步方法用的都是一把锁 -> 实例对象本身
 *          对于静态同步方法，锁的是当前类的Class对象，如Phone.class唯一的一个模板
 *          对于同步方法块，锁的是 synchronized 括号内的对象
 *
 *          7-8
 *          当一个线程试图访问同步代码时它首先必须得到锁，正常退出或抛出异常时必须释放锁
 *
 *          所有的普通同步方法用的都是同一把锁——实例对象本身，就是new出来的具体实例对象本身，本类this
 *          也就是说如果一个实例对象的普通同步方法获取锁后，该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁
 *
 *          所有的静态同步方法用的也是同一把锁——类对象本身，就是我们说过的唯一模板Class
 *          具体实例对象this和唯一模板Class，这两把锁是两个不同的对象，所以静态同步方法与普通同步方法之间是不会有竞态条件的
 *          但是一旦一个静态同步方法获取锁后，其他的静态同步方法都必须等待该方法释放锁后才能获取锁
 *
 */
public class Lock8Demo {
    public static void main(String[] args) throws Exception {
        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "a").start();

        // 暂停毫秒，保证a线程先启动
        TimeUnit.MILLISECONDS.sleep(200);

        new Thread(() -> {
//             phone.sendSMS();
//             phone.hello();
             phone2.sendSMS();
        }, "b").start();
    }
}

class Phone { // 资源类

    // synchronized 悲观锁，同一时间有且只有一个线程能够进入这个资源类
    // synchronized 锁的是资源类，不是锁这个方法，锁的是这个方法上面的资源类
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("-----sendEmail发送邮件");
    }

    public synchronized void sendSMS() {
        System.out.println("-----sendSMS发送短信");
    }

    // 并没有争抢 - 不产生静态条件
    public void hello() {
        System.out.println("-----hello");
    }
}
